Tyler Rarick
import IPython.display as ipd # For in-notebook audio
import numpy as np # Nice data structures and math operations
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
fs = 44100 # Sampling rate
duration = 2 # Duration
fref = 440 # Reference frequency
note = 3 # C4
class Song:
def __init__(self, fs=44100, fref=440, bpm=120):
self.fs = fs
self.fref= fref
self.bpm = bpm
self.df = pd.DataFrame(columns=['start', 'note',
'duration', 'intensity'])
return
def __BarToSamples(self):
return int(self.__BarToSeconds()*self.fs)
def __BarToSeconds(self):
return 4*self.__BeatToSeconds()
def __BeatToSeconds(self):
return (1/self.bpm)*60
# Duration and start in bars
def AddNote(self, start, note, duration=0.25, intensity=0.1):
newNote = pd.DataFrame([[start, note, duration, intensity]],
columns=['start', 'note',
'duration', 'intensity'])
self.df = self.df.append(newNote)
return
def MajorToNote(self):
return
def Compile(self, instrument):
df = self.df.copy()
barSamples = self.__BarToSamples()
arraySize = (df['start'] + df['duration']/4).max()*barSamples
arraySize = np.ceil(arraySize).astype(np.int)
x = np.zeros(arraySize)
df['note'] = df['note'].apply(Song.__NoteToFreq, args=(self.fref,))
df['start'] = df['start']*barSamples
df['duration'] = df['duration']*self.__BeatToSeconds()
for i, note in df.iterrows():
subArray = instrument.play(note['note'],
self.fs,
note['duration'],
note['intensity'])
start = np.ceil(note['start']).astype(np.int)
end = start + subArray.size
x[start:end] += subArray
return 0.5*x/max(x)
def __NoteToFreq(note, fref):
return fref*np.power(2, note/12)
class Instrument:
def __init__(self):
if self.__class__ == Instrument:
raise NotImplementedError
# Duration in seconds
def play(self, f, fs, duration, intensity):
if self.__class__ == Instrument:
raise NotImplementedError
def EnvelopeView(subClass):
return subClass.play(1, 100, 1, 1)
class SineWave(Instrument):
# Duration in seconds
def play(f, fs, duration, intensity):
samples = fs*duration
t = np.linspace(0,duration, samples)
w = 2*np.pi*f
return intensity*np.sin(w*t)
def EnvelopeView():
return Instrument.EnvelopeView(SineWave)
class SawTooth(Instrument):
# Duration in seconds
def play(f, fs, duration, intensity):
samples = fs*duration
t = np.linspace(0,duration, samples)
T = 1/f
# t mod T represents progress to T
# Amplitude scaled by /T*intensity
return intensity*(t % T)/T
def EnvelopeView():
return Instrument.EnvelopeView(SawTooth)
class SquareWave(Instrument):
# Duration in seconds
def play(f, fs, duration, intensity):
samples = fs*duration
t = np.linspace(0,duration, samples)
T = 1/f
# Same as Sawtooth but rounded to integer 0/1
return intensity*np.round((t % T)/T)
def EnvelopeView():
return Instrument.EnvelopeView(SquareWave)
class TriangleWave(Instrument):
# Duration in seconds
def play(f, fs, duration, intensity):
samples = fs*duration
t = np.linspace(0,duration, samples)
T = 1/f
# 2*abs(square wave - sawtooth)
return 2*intensity*abs(np.round((t % T)/T) - (t % T)/T)
def EnvelopeView():
return Instrument.EnvelopeView(TriangleWave)
class Snare(Instrument):
def play(f, fs, duration, intensity):
return np.random.rand(np.ceil(fs*duration).astype(np.int))
def EnvelopeView():
return Instrument.EnvelopeView(Snare)
song = Song(fs=44100, fref=440, bpm=120)
song.AddNote(0, 0, duration=8)
song.AddNote(0.25, 2, duration=1)
song.AddNote(0.5, 4, duration=6)
song.AddNote(0.75, 5, duration=1)
song.AddNote(1, 7, duration=4)
song.AddNote(1.25, 9, duration=1)
song.AddNote(1.5, 11, duration=1)
song.AddNote(1.75, 12, duration=1)
song.AddNote(2, 12-12, duration=1)
song.AddNote(2.25, 11-12, duration=1)
song.AddNote(2.5, 9-12, duration=1)
song.AddNote(2.75, 7-12, duration=13)
song.AddNote(3, 5-12, duration=1)
song.AddNote(3.25, 4-12, duration=11)
song.AddNote(3.5, 2-12, duration=1)
song.AddNote(3.75, 0-12, duration=9)
song.AddNote(3.75, 0-24, duration=9)
x = song.Compile(instrument=SineWave)
plt.plot(SineWave.EnvelopeView())
ipd.Audio(x, rate=fs)
x = song.Compile(instrument=SawTooth)
plt.plot(SawTooth.EnvelopeView())
ipd.Audio(x, rate=fs)
x = song.Compile(instrument=SquareWave)
plt.plot(SquareWave.EnvelopeView())
ipd.Audio(x, rate=fs)
x = song.Compile(instrument=TriangleWave)
plt.plot(TriangleWave.EnvelopeView())
ipd.Audio(x, rate=fs)
drumSong = Song(fs=44100, fref=440, bpm=120)
for i in range(16):
drumSong.AddNote(i*0.25, 1, duration=0.05)
d = drumSong.Compile(instrument=Snare)
plt.plot(Snare.EnvelopeView())
ipd.Audio(d, rate=fs)